/*
 * File Name: httpstuff.c
 *
 * Group Members: Cassie Chin (cassiechin9793@gmail.com)
 *                Stephen Smetana (stephensmetana11@gmail.com)
 *                Logan Widick (logan.widick@gmail.com)
 *
 * File Description:
 *
 *
 */

#include "httpstuff.h" 
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/select.h>

#include <assert.h>

#define DEFAULT_HTTP_PORT "80"
#define DEFAULT_FILE_NAME "index.html"
#define MIN_BUFFERLEN 5
#define TIMEOUT_SEC 5
#define TIMEOUT_USEC 10

#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
/**
 * Contains the status code, the reason-phrase, and whether or not a body can be found.
 */
static HTTPResponseCode responseCodes[] = { { 100, "Continue", false }, { 101,
		"Switching Protocols", false }, { 200, "OK", false }, { 201, "Created",
		false }, { 202, "Accepted", false }, { 203,
		"Non-Authoritative Information", false }, { 204, "No Content", false },
		{ 205, "Reset Content", false }, { 206, "Partial Content", false }, {
				300, "Multiple Choices", true }, { 301, "Moved Permanently",
				true }, { 302, "Found", true }, { 303, "See Other", true }, {
				304, "Not Modified", false }, { 305, "Use Proxy", true }, { 306,
				"(Unused)", true }, { 307, "Temporary Redirect", true }, { 400,
				"Bad Request", true }, { 401, "Unauthorized", true }, { 402,
				"Payment Required", true }, { 403, "Forbidden", true }, { 404,
				"Not Found", true }, { 405, "Method Not Allowed", true }, { 406,
				"Not Acceptable", true }, { 407,
				"Proxy Authentication Required", true }, { 408,
				"Request Timeout", true }, { 409, "Conflict", true }, { 410,
				"Gone", true }, { 411, "Length Required", true }, { 412,
				"Precondition Failed", true }, { 413,
				"Request Entity Too Large", true }, { 414,
				"Request-URI Too Long", true }, { 415, "Unsupported Media Type",
				true }, { 416, "Requested Range Not Satisfiable", true }, { 417,
				"Expectation Failed", true }, { 500, "Internal Serval Error",
				true }, { 501, "Not Implemented", true }, { 502, "Bad Gateway",
				true }, { 503, "Service Unavailable", true }, { 504,
				"Gateway Timeout", true }, { 505, "HTTP Version Not Supported",
				true },

};

/**
 * Returns the reason-phrase for the code
 * @param code
 * @return a string. DO NOT FREE THIS!
 */
char* getReasonPhraseForCode(int code) {
	int maxSize = sizeof(responseCodes) / sizeof(HTTPResponseCode);
	int min = 0;
	int mid = maxSize / 2;
	int max = maxSize - 1;
	while (min <= max) {
		mid = max + mid / 2;
		if (responseCodes[mid].num == code) {
			return responseCodes[mid].desc;
		}
		//mid > code; go back
		else if (responseCodes[mid].num > code) {
			max = mid - 1;
		}
		//mid < code; go up
		else if (responseCodes[mid].num < code) {
			min = mid + 1;
		}
	}

	//if we are still here issue default code.
	//1xx Informational
	if (code / 100 == 1) {
		return "Informational Request Received Continuing";
	}
	//2xx success
	else if (code / 100 == 2) {
		return "Successful";
	}
	//3xx Redirect
	else if (code / 100 == 3) {
		return "Redirect";
	}
	//4xx client error
	else if (code / 100 == 4) {
		return "Client Error";
	}
	//5xx Server Error
	else if (code / 100 == 5) {
		return "Informational Request Received Continuing";
	}
	//ok this should not happen.
	else {
		return NULL;
	}
}

bool isBodyOk(int code) {
	int maxSize = sizeof(responseCodes) / sizeof(HTTPResponseCode);
	int min = 0;
	int mid = maxSize / 2;
	int max = maxSize - 1;
	while (min <= max) {
		mid = max + mid / 2;
		if (responseCodes[mid].num == code) {
			return responseCodes[mid].canHaveBody;
		}
		//mid > code; go back
		else if (responseCodes[mid].num > code) {
			max = mid - 1;
		}
		//mid < code; go up
		else if (responseCodes[mid].num < code) {
			min = mid + 1;
		}
	}

	//assume we can
	return true;
}

/******************************************************************************/
/*                         START HEADER STUFF                                 */
/******************************************************************************/
/**
 * Constructor
 * @return a new header list
 */
HeaderList* new_HeaderList() {
	HeaderList *self = calloc(1, sizeof(HeaderList));

	self->head = NULL;
	self->tail = NULL;

	return self;
}

/**
 * Destructor
 * @param list the header list to destroy
 */
void delete_HeaderList(HeaderList *list) {
	HeaderListNode *nxt_iter = NULL;
	HeaderListNode *iter = list->head;

	while (iter != NULL) {
		nxt_iter = iter->next;
		if (iter->name)
			free(iter->name);
		if (iter->value)
			free(iter->value);
		if (iter)
			free(iter);
		iter = nxt_iter;
	}
	if (list)
		free(list);
}

/**
 * Adds a new header given the name and value
 * @param list
 * @param name
 * @param value
 */
void HeaderList_addHeader(HeaderList *list, char *name, char *value) {
	if (!list)
		list = new_HeaderList();
	if (!list)
		return;

	// Allocate memory for the node and set values
	HeaderListNode *ptr2HeaderNode = (HeaderListNode*) calloc(1,
			sizeof(HeaderListNode));
	ptr2HeaderNode->name = strdup(name);
	ptr2HeaderNode->value = strdup(value);
	ptr2HeaderNode->next = NULL;

	if (list->tail) {
		// Set the current tail's next pointer, to point to the new node.
		list->tail->next = ptr2HeaderNode;
		// Set the tail of the list to the new node.
		list->tail = ptr2HeaderNode;
	} else {
		list->head = ptr2HeaderNode;
		list->tail = ptr2HeaderNode;
	}
}

/**
 * returns true if header exists
 * @param list
 * @param name
 * @return
 */
bool HeaderList_doesHeaderExist(HeaderList *list, char *name) {
	if (!list) {
		return false;
	}
	HeaderListNode *iter = list->head;
	for (iter = list->head; iter != NULL; iter = iter->next) {
		if (!iter->name || !iter->value)
			continue;
		if (strcmp(name, iter->name) == 0) {
			return true;
		}
	}
	return false;
}

void HeaderList_print(HeaderList *list, FILE *theFile)
{
	HeaderListNode *iter = list->head;
	for (iter = list->head; iter != NULL; iter = iter->next) {
		printf("%s: %s\n", iter->name, iter->value);
	}
}
/**
 * Returns a pointer to the value for a header.
 * THIS METHOD DOES NOT COPY SO PLEASE DO NOT ALTER THE RETURNED VALUE. DO NOT FREE.
 * @param list the header list
 * @param name the name of the header.
 */
char *HeaderList_getValue(HeaderList *list, char *name) {
	if (!list) {
		return NULL;
	}
	HeaderListNode *iter = list->head;
	for (iter = list->head; iter != NULL; iter = iter->next) {
		if (!iter->name || !iter->value)
		{
			continue;
		}
		else if (strcmp(name, iter->name) == 0) {
			return iter->value;
		}
	}
	return NULL;
}

void HeaderList_modifyHeaderWithKey(HeaderList *list, char *name, char *value)
{
	if(!list || !name || !value) return;
	HeaderListNode *iter = list->head;
	for (iter = list->head; iter != NULL; iter = iter->next) {
		
		if (strcmp(name, iter->name) == 0) {
			char *new_value = strdup(value);
			if(new_value)
			{
				free(iter->value);
				iter->value = new_value;
			}
			else assert(false);
			return;
		}
	}
	
}

/**
 * Appends a new value to the last header in the list.
 * @param list
 * @param value
 */
void HeaderList_addToLastHeader(HeaderList *list, char *value) {
	if (!list || !value)
		return;
	if (!list->tail)
		return;

	char *tmpbuf = (char *) calloc(
			strlen(value) + strlen(list->tail->value) + 2, sizeof(char));
	if (!tmpbuf)
		return;

	/*
	 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
	 * Multiple message-header fields with the same field-name MAY be present
	 * in a message if and only if the entire field-value for that header field
	 * is defined as a comma-separated list [i.e., #(values)].
	 * It MUST be possible to combine the multiple header fields into one
	 * "field-name: field-value" pair, without changing the semantics of the message,
	 * by appending each subsequent field-value to the first, each separated by a comma.
	 * The order in which header fields with the same field-name are received is therefore
	 * significant to the interpretation of the combined field value, and thus a proxy
	 * MUST NOT change the order of these field values when a message is forwarded.
	 */
	strcat(tmpbuf, list->tail->value);
	strcat(tmpbuf, ",");
	strcat(tmpbuf, value);

	//we have a new buffer, so out with the old
	if (list->tail->value)
		free(list->tail->value);

	//and in with the new
	list->tail->value = tmpbuf;
}

/******************************************************************************/
/*                         END HEADER STUFF                                   */
/******************************************************************************/

/*****************************************************************************/
/*                        BEGIN FILE PTR SET STUFF                           */
/*****************************************************************************/

/**
 * Opens a client connection given the port, ip, and a pointer to a FilePointerSet to be filled
 * @param PORT the port # as a string
 * @param IP the IP address to connect to
 * @param ptrSet the pointer to the FilePointerSet to fill.
 * @return 0 on success, something negative on fail.
 */
int openClientConnection(char *PORT, char *IP, FilePointerSet *ptrSet) {
	if (!PORT || !IP || !ptrSet)
		return -1;

	//Connection code adapted from 
	//http://beej.us/guide/bgnet/output/html/multipage/clientserver.html#simpleclient

	struct addrinfo hints, *servinfo, *p;
	int sockfd = 0;
	int rv = 0;

	memset(&hints, 0, sizeof hints);
	memset(ptrSet, 0, sizeof(FilePointerSet));

	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if ((rv = getaddrinfo(IP, PORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return -1;
	}

	// loop through all the results and connect to the first we can
	for (p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
				== -1) {
			perror("client: socket");
			continue;
		}

		if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("client: connect");
			continue;
		}

		break;
	}
	if (p == NULL) {
		fprintf(stderr, "client: failed to connect\n");
		return -2;
	}

	//now set the file.
	ptrSet->inFile = fdopen(sockfd, "r");
	ptrSet->outFile = fdopen(dup(sockfd), "w");
	setvbuf(ptrSet->inFile, NULL, _IONBF, 0);
	setvbuf(ptrSet->outFile, NULL, _IONBF, 0);
	return 0;
}
/*****************************************************************************/
/*                        END   FILE PTR SET STUFF                           */
/*****************************************************************************/

/******************************************************************************/
/*                         START HTTP STUFF                                   */
/******************************************************************************/

/**
 * Constructor
 * @return
 */
HTTPMsg *new_HTTPMsg() {
	HTTPMsg *self = (HTTPMsg *) calloc(1, sizeof(HTTPMsg));
	if (!self)
		return NULL;

	//the other fields are set to null via calloc.
	self->headerList = new_HeaderList();
	self->contentLength = CONTENT_LENGTH_NOT_SET;
	self->version = 1.0;
	return self;
}

/**
 * destructor
 * @param msg
 */
void delete_HTTPMsg(HTTPMsg *msg) {
	if (msg->method)
		free(msg->method);
	if (msg->headerList)
		delete_HeaderList(msg->headerList);
	if (msg->resource)
		free(msg->resource);
	if (msg->fileptr)
		fclose(msg->fileptr);
	if(msg->port)
	      free(msg->port);
	if(msg->path) 
		free(msg->path);
	if(msg->host) 
		free(msg->host);
	if(msg->reasonPhrase)
		free(msg->reasonPhrase);
	if (msg)
		free(msg);
}

/**
 * Determines if a character is hexadecimal
 * @param ch
 * @return true if hex, false if not.
 */
bool isHex(char ch) {
	if (ch >= '0' && ch <= '9')
		return true;
	else if (ch >= 'a' && ch <= 'f')
		return true;
	else if (ch >= 'A' && ch <= 'F')
		return true;
	else
		return false;
}

/**
 * Decodes a URL
 * @param url
 * @return A new char* for the decoded url.
 */
char *decode_url(char *url) {
	if (!url)
		return NULL;
	int len = strlen(url);
	char *retbuf = calloc(len + 1, sizeof(char));
	int i = 0;
	int j = 0;
	char hexbuf[3];
	int chars_after_perc = 0;
	for (i = 0; i < len; i++) {
		//we see percent char, but are still trying to decode the previous percent encoded. Error.
		if (url[i] == '%' && chars_after_perc != 0) {
			free(retbuf);
			return NULL;
		}
		//we see percent char; begin percent encode state
		else if (url[i] == '%') {
			chars_after_perc = 2;
		}
		//we see hex and expect chars after perc. Add to hexbuffer
		else if (isHex(url[i]) && chars_after_perc > 0) {
			hexbuf[2 - chars_after_perc] = url[i];
			chars_after_perc--;
			hexbuf[2] = '\0';

			//we have finished filling hexbuffer. Scan and store in retbuf
			if (chars_after_perc == 0) {
				int num = 0;
				sscanf(hexbuf, "%x", &num);
				retbuf[j] = (char) num;
				j++;
			}
		}

		//we see nonhex and expect hex. Error!
		else if (!isHex(url[i]) && chars_after_perc != 0) {
			free(retbuf);
			return NULL;
		}

		//we see a plus sign. Copy as space.
		else if (url[i] == '+') {
			retbuf[j] = ' ';
			j++;
		}

		//copy over and continue
		else {
			retbuf[j] = url[i];
			j++;
		}
	}
	return retbuf;
}

/**
 * Extracts the path portion of a URL.
 * Examples:
 * http://www.google.com/folder/file.html will obtain /folder/file.html
 * www.google.com/folder/file.html will obtain /folder/file.html
 * google.com:8080/folder/file.html will obtain /folder/file.html
 * @param msg
 * @param theresource
 * @return the path portion of the url.
 */
char* HTTPMsg_extractPathPortionOfURL(HTTPMsg *msg, char *theresource) {
	if (!theresource || !msg)
		return NULL;

	/*
	 * Explanation:
	 * Group 0: 		the whole thing
	 * SubGroup 1: 		name of url scheme with colon but no slashes
	 * SubGroup 2: 		name of url scheme with no colon or slashes
	 * SubGroup 3: 		the name of the website with two slashes in front of it
	 * SubGroup 4: 		the name of the website with no slashes in front
	 * SubGroup 5: 		Either the path to the resource
	 * 			starting from the root of the site (/)
	 * 			or the name of the website (no slashes)
	 *			and the path to the resource starting at the root
	 *			(THIS IS THE BIG ONE HERE!!!!)
	 * SubGroups 6-7:	mainly used for supporting special features
	 * 			like questionmarks and pound targets.
	 *
	 * This regex was taken from http://tools.ietf.org/html/rfc3986#page-50
	 * but modified so that it would actually compile and match stuff.
	 *
	 * For this function, only subgroup 5 is relevant.
	 */
	regex_t regex;
	regcomp(&regex, "^(([^.:/?#]+):)?(//([^/?#]*))?([^?#]*)([^#]*)?(#(.*))?",
			REG_EXTENDED);
	regmatch_t *matches = calloc(regex.re_nsub + 1, sizeof(regmatch_t));
	char *match_buffer = NULL;

	regex_t subregex;

	/*
	 * The subregex to weed out stuff that appears before the path
	 * (like google.com in google.com/folder)
	 * Again, only SubGroup 5 is important.
	 */
	regcomp(&subregex, "(([^/?#])*)?((/)*)(([^#?])*)?", REG_EXTENDED);
	regmatch_t *submatches = calloc(subregex.re_nsub, sizeof(regmatch_t));

	//regexec returns 0 if successful
	if (regexec(&regex, theresource, regex.re_nsub, matches, 0) == 0) {
		//copy into new string
		char *new_buffer = calloc(matches[5].rm_eo - matches[5].rm_so + 1,
				sizeof(char));
		strncpy(new_buffer, &(theresource[matches[5].rm_so]),
				matches[5].rm_eo - matches[5].rm_so);

		//now apply the subregex
		if (regexec(&subregex, new_buffer, subregex.re_nsub, submatches, 0)
				== 0) {
			//copy into match buffer
			match_buffer = calloc(submatches[5].rm_eo - submatches[5].rm_so + 5,
					sizeof(char));
			strcpy(match_buffer, "/");
			strncat(match_buffer, &(new_buffer[submatches[5].rm_so]),
					(submatches[5].rm_eo - submatches[5].rm_so));

		}

		//handle url decode
		char *ret_buffer = decode_url(match_buffer);
		if (!ret_buffer) {
			free(match_buffer);
			match_buffer = NULL;
		} else {
			free(match_buffer);
			match_buffer = ret_buffer;
		}

		if (new_buffer)
			free(new_buffer);
	}

	//cleanup
	regfree(&regex);
	if (matches)
		free(matches);
	regfree(&subregex);
	if (submatches)
		free(submatches);

	//exit
	return (match_buffer);
}

/**
 * Gets the target info from the first folder in a URL.
 * Examples:
 * /www.example.com/file.html will set host=www.example.com, port=80
 * /www.example.com:8080/file.html will set host=www.example.com, port=8080
 * @param msg the HTTP message.
 * @param url the url.
 * @return
 * 		A pointer to the string starting AFTER the first folder. DO NOT FREE.
 * 		The two above examples will both return "/file.html"
 */
char* HTTPMsg_getTargetInfoFromFirstFolderInURL(HTTPMsg *msg, char *url) {

	fprintf(stderr, "getTargetInfoFromFirstFolder called with %s\n", url);
	int firstSlashInd = 0;
	int nextSlashInd = -1;
	int i = 0;
	int len = 0;
	int endingColonInd = -1;

	if (!msg || !url)
		return NULL;

	if (url[0] != '/')
		firstSlashInd = -1;

	//now try to find next slash.
	len = strlen(url);
	for (i = firstSlashInd + 1; i < len; i++) {
		if (url[i] == '/') {
			nextSlashInd = i;
			break;
		}
	}

	//if we do not have a next slash, then the next slash is the end of this string. 
	if (nextSlashInd == -1)
		nextSlashInd = len;

	//find the ending colon
	for (i = nextSlashInd - 1; i > firstSlashInd; i--) {
		if (url[i] == ':') {
			endingColonInd = i;
			break;
		}
	}

	//no ending colon; port = default; host = the whole substring
	if (endingColonInd == -1) {
		msg->host = calloc(nextSlashInd - firstSlashInd + 2, sizeof(char));
		memmove(msg->host, &(url[firstSlashInd + 1]),
				(nextSlashInd - (firstSlashInd + 1)) * sizeof(char));

		msg->port = strdup(DEFAULT_HTTP_PORT);
		fprintf(stderr, "msg->host %s, msg->port %s\n", msg->host, msg->port);
	}

	//ending colon; port=what appears after colon. host=what appears before colon
	else {
		msg->host = calloc(endingColonInd - firstSlashInd + 2, sizeof(char));
		memmove(msg->host, &(url[firstSlashInd + 1]),
				(endingColonInd - (firstSlashInd + 1)) * sizeof(char));

		msg->port = calloc(nextSlashInd - endingColonInd + 2, sizeof(char));
		memmove(msg->port, &(url[endingColonInd + 1]),
				(nextSlashInd - (endingColonInd + 1)) * sizeof(char));
		fprintf(stderr, "msg->host %s, msg->port %s\n", msg->host, msg->port);
	}

	return &(url[nextSlashInd]);
}

/**
 * Extracts the portion of the URL for the host and port.
 * Examples:
 * http://www.google.com/file.html will return a string starting with "www.google.com"
 * http://www.google.com:8080/file.html will return a string starting with "www.google.com:8080"
 * www.google.com:80/file.html will return a string starting with "www.google.com:80"
 * www.google.com:80/file.html
 * @param msg
 * @param theresource
 * @return a new string for the portion of the URL that contains the host and port.
 */
char* HTTPMsg_extractHostPortPortionOfURL(HTTPMsg *msg, char *theresource) {
	assert(theresource);
	assert(msg);
	/*
	 * Explanation:
	 * Group 0: the whole thing
	 * SubGroup 1: 		name of url scheme with colon but no slashes
	 * SubGroup 2: 		name of url scheme with no colon or slashes
	 * SubGroup 3: 		the name of the website with two slashes in front of it
	 * SubGroup 4: 		the name of the website with no slashes in front
	 * SubGroup 5: 		Either the path to the resource 
	 *			starting from the root of the site (/)
	 * 			or the name of the website (no slashes)
	 *			and the path to the resource starting at the root
	 * SubGroups 6-7:	mainly used for supporting special features 
	 *			like questionmarks and pound targets.
	 *
	 * This regex was taken from http://tools.ietf.org/html/rfc3986#page-50
	 * but modified so that it would actually compile and match stuff.
	 *
	 * For this function, try to use subgroup 4.
	 * If subgroup 4 is not present, return subgroup 5.
	 */
	regex_t regex;
	regcomp(&regex, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)([^#]*)?(#(.*))?",
			REG_EXTENDED);
	regmatch_t *matches = calloc(regex.re_nsub + 1, sizeof(regmatch_t));
	char *match_buffer = NULL;

	//regexec returns 0 if successful
	if (regexec(&regex, theresource, regex.re_nsub, matches, 0) == 0) {
		//no difference in place means that match did not exist
		if (matches[4].rm_eo - matches[4].rm_so == 0) {
			fprintf(stderr, "Match[4] not found\n");
			//if grp 4 is not present, use grp 5.
			if (matches[5].rm_eo - matches[5].rm_so > 0) {
				fprintf(stderr, "Match[5] found\n");
				match_buffer = calloc(matches[5].rm_eo - matches[5].rm_so + 1,
						sizeof(char));
				assert(match_buffer);
				memmove(match_buffer, &(theresource[matches[5].rm_so]),
						(matches[5].rm_eo - matches[5].rm_so) * sizeof(char));
			}
			else { assert(false);}
		}
		//group 4 is present; use it!
		else if(matches[4].rm_eo - matches[4].rm_so > 0) {
			fprintf(stderr, "Match[4] found\n");
			match_buffer = calloc(matches[4].rm_eo - matches[4].rm_so + 1,
					sizeof(char));
			assert(match_buffer);
			memmove(match_buffer, &(theresource[matches[4].rm_so]),
					(matches[4].rm_eo - matches[4].rm_so) * sizeof(char));
		}
		else assert(false);
	}
	else { assert(false);}

	//cleanup
	regfree(&regex);
	if (matches)
		free(matches);

	//exit
	return match_buffer;
}

/**
 * Sets the HTTPMsg to use a given URL for making the host, port, path, etc.
 * @param msg the HTTPMsg.
 * @param theresource the URL to use.
 */
void HTTPMsg_useResource(HTTPMsg *msg, char *theresource, bool useFirstFolderAsLoc) {
	if (!msg || !theresource)
		return;

	//we already set the info; exit.
	if(msg->port) return;

	char *pathPortion = NULL;
	char *resourcePortion = NULL;

	if(useFirstFolderAsLoc){
	/*
	 * we are to use the
	 * proxy:proxy_port/dest:dest_port/path_on_dest
	 * convention.
	 * This means that the dest:dest_port (the port and host to use)
	 * are buried within the path (which contains all but proxy:proxy_port)
	 */
	//get the proxy path
	pathPortion = HTTPMsg_extractPathPortionOfURL(msg, theresource);
	fprintf(stderr, "useFirstFolderAsLoc pathPortion %s\n", pathPortion);

	//use the first folder in the proxy path as the host/port combo
	char *pathPortion2= HTTPMsg_getTargetInfoFromFirstFolderInURL(msg, pathPortion);
	fprintf(stderr, "useFirstFolderAsLoc pathPortion2 %s\n", pathPortion2);
	//copy into a new string
	msg->path = strdup(pathPortion2);

	//clean up.
	free(pathPortion);
	}
	else{	

	fprintf(stderr, "useResource start: %s\n", theresource);
	/*
	 * Regular convention: get resource and path separately
	 */
	//get the resource portion
	resourcePortion = HTTPMsg_extractHostPortPortionOfURL(msg, theresource);
	assert(resourcePortion);
	int i = strlen(resourcePortion)-1;
	int lastColonInd = -1;	
	for(; i>= 0; i--)
	{
		if(resourcePortion[i] == ':'){
			lastColonInd = i;
			break;
		}
	}
	if(lastColonInd < 0)
	{
		msg->host = strdup(resourcePortion);
		msg->port = strdup(DEFAULT_HTTP_PORT);
	}
	else{
		resourcePortion[lastColonInd] = '\0';
		msg->host = strdup(resourcePortion);
		msg->port = strdup(&(resourcePortion[lastColonInd+1]));
	}
	fprintf(stderr, "useResource resourcePortion: %s, msg->port %s, msg->host %s\n", resourcePortion, msg->port, msg->host);

	//get the portion of the url containing the path for the resource
	pathPortion = HTTPMsg_extractPathPortionOfURL(msg, theresource);
	fprintf(stderr, "useResource pathPortion: %s\n", pathPortion);
	
	//copy into a new string
	msg->path = strdup(pathPortion);
	
	//cleanup
	free(resourcePortion);
	free(pathPortion);
	}
}

/**
 * Use the "Host" header to set the host and port info.
 * @param msg the HTTPMsg.
 */
void HTTPMsg_useHost(HTTPMsg *msg){
	if(!msg) return;

	//we have already set the port; exit.
	if(msg->port) return;

	//get the Host header.
	char *headerVal = HeaderList_getValue(msg->headerList, "Host");
	if(!headerVal) return;

	//this should get the entire host string.
	char *resourcePortion = HTTPMsg_extractHostPortPortionOfURL(msg, headerVal);

	//use the value of the resource portion.
	HTTPMsg_getTargetInfoFromFirstFolderInURL(msg, resourcePortion);

	//For the path, use the resource specified in the request-uri for the get
	char *pathPortion = HTTPMsg_extractPathPortionOfURL(msg, msg->resource);
	msg->path = strdup(pathPortion);

	//clean up
	free(pathPortion);
	free(resourcePortion);
}

/**
 * Tries every way to load the host, path, and port info for a message.
 * @param msg
 * @param useFirstFolderAsLoc if true, this is the proxy
 */
void HTTPMsg_loadHostPathAndPort(HTTPMsg *msg, bool useFirstFolderAsLoc){
  if(useFirstFolderAsLoc){
	/* If we are to follow the
	 * proxy:proxy_port/dest_host:dest_port/path_on_dest
	 * convention, then all we can use is the GET request-uri.
	 */
	HTTPMsg_useResource(msg, msg->resource, true);
  }
  else
  {
	/*
	 * If we are not to follow that convention, use the following order of precedence:
	 * Location
	 * Content-Location
	 * Host
	 * The Request-URI in the GET
	 */
	HTTPMsg_useResource(msg, HeaderList_getValue(msg->headerList, "Location"), false);
	if(msg->path) return;

	HTTPMsg_useResource(msg, HeaderList_getValue(msg->headerList, "Content-Location"), false);
	if(msg->path) return;
	
	HTTPMsg_useHost(msg);
	if(msg->path) return; //check if we have obtained the path.
	
	HTTPMsg_useResource(msg, msg->resource, false);
  }
}

/**
 * Returns true if the URL contains an argument section.
 * If the URL contains an argument section, we probably should not cache it. 
 * @param msg the message
 * @param theresource the URL to test.
 * @return true if an argument section is present; otherwise false. 
 */
bool HTTPMsg_doesResourceContainArgSection(HTTPMsg *msg, char *theresource)
{
	if (!theresource || !msg)
		return false;
	/*
	 * Explanation:
	 * Group 0: the whole thing
	 * SubGroup 1: 		name of url scheme with colon but no slashes
	 * SubGroup 2: 		name of url scheme with no colon or slashes
	 * SubGroup 3: 		the name of the website with two slashes in front of it
	 * SubGroup 4: 		the name of the website with no slashes in front
	 * SubGroup 5: 		Either the path to the resource starting from the root of the site (/)
	 * 			or the name of the website (no slashes)
	 *			and the path to the resource starting at the root
	 * SubGroups 6-7: 	mainly used for supporting special features 
	 *			like questionmarks and pound targets.
	 *
	 * This regex was taken from http://tools.ietf.org/html/rfc3986#page-50
	 * but modified so that it would actually compile and match stuff.
	 *
	 * For this function, check that subgroup 6 is present. 
	 */
	regex_t regex;
	regcomp(&regex, "^(([^.:/?#]+):)?(//([^/?#]*))?([^?#]*)([^#]*)?(#(.*))?",
			REG_EXTENDED);
	regmatch_t *matches = calloc(regex.re_nsub + 1, sizeof(regmatch_t));

	//regexec returns 0 if successful
	if (regexec(&regex, theresource, regex.re_nsub, matches, 0) == 0) {
		//no difference in place means that match did not exist
		if (matches[6].rm_eo - matches[6].rm_so == 0) {
			return false;
		}
		//group 6 is present; use it!
		else {
			return true;
		}
	}

	//cleanup
	regfree(&regex);
	if (matches)
		free(matches);

	return false;
}

/**
 * Attempts to guess if the message is dynamic. 
 * It is assumed that a URL is not dynamic unless it contains an argument section. 
 * @param msg the HTTPMsg. 
 */
bool HTTPMsg_isDynamic(HTTPMsg *msg, bool useFirstFolderAsLoc)
{
  if(useFirstFolderAsLoc){
  /* If we are to follow the
	 * proxy:proxy_port/dest_host:dest_port/path_on_dest
	 * convention, then all we can use is the GET request-uri.
	 */
	if(HTTPMsg_doesResourceContainArgSection(msg, msg->resource)) return true;
	else return false;
  }else{
	/*
	 * If we are not to follow that convention, use the following order of precedence:
	 * Content-Location
	 * The Request-URI in the GET
	 */
	if(HTTPMsg_doesResourceContainArgSection(msg, HeaderList_getValue(msg->headerList, "Content-Location"))) return true;
	else if(HTTPMsg_doesResourceContainArgSection(msg, msg->resource)) return true;
	else return false;
  }

}

void HTTPMsg_print(HTTPMsg *msg, FILE* theFile)
{
	if(!msg) return;
	fprintf(theFile, "-------- MESSAGE DUMP BEGIN ------------ \n");
	fprintf(theFile, "IsRequest: %d\n", msg->isRequest);
	if(msg->method) 
		fprintf(theFile, "MethodInStruct: %s\n", msg->method);	
	if(msg->resource)
		fprintf(theFile, "ResourceInStruct: %s\n", msg->resource);
	if(msg->port)
		fprintf(theFile, "PortInStruct: %s\n", msg->port);
	if(msg->path)
		fprintf(theFile, "PathInStruct: %s\n", msg->path);
	if(msg->host)
		fprintf(theFile, "HostInStruct: %s\n", msg->host);
	if(msg->reasonPhrase)
		fprintf(theFile, "ReasonPhraseInStruct: %s\n", msg->reasonPhrase);
	fprintf(theFile, "ContentLengthInStruct: %d\n", msg->contentLength);
	fprintf(theFile, "StatusCodeInStruct: %d\n", msg->statusCode);
	fprintf(theFile, "VersionInStruct: %f\n", msg->version);

	HeaderList_print(msg->headerList, theFile);
	fprintf(theFile, "--------- MESSAGE DUMP END ---------------\n");
}	

/**
 * Extracts the path to the file found in the message's resource. This is mainly used by proxies. 
 * @param msg the message
 * @return
 * 		the path to the file on the server
 * 		(assuming that root is the current directory),
 * 		or NULL if the url is dynamic or cannot be parsed.
 */
char* HTTPMsg_findFilePath(HTTPMsg *msg, bool useFirstFolderAsLoc) {
	if (!msg)
		return NULL;

	//first, ensure that the host, path, port are good.
	HTTPMsg_loadHostPathAndPort(msg, useFirstFolderAsLoc);

	//if we still do not have a port # then the url is no good. 
	if(!msg->port) return NULL;

	//next, check that the URL is not dynamic.
	if(HTTPMsg_isDynamic(msg, useFirstFolderAsLoc)) return NULL;

	//then, create new string.
	int len = strlen(msg->host) + strlen(msg->path) + strlen(DEFAULT_FILE_NAME) + 8;
	char *retStr = calloc(len, sizeof(char));
	if(!retStr) return NULL;
	
	//copy into new string
	strcpy(retStr, "./");
	strcat(retStr, msg->host);
	strcat(retStr, msg->path);

	//check if the last item represents a file.
	int i = strlen(msg->path)-1;
	int slashIndex = -1;
	int dotIndex = -1;
	for( ; i >= 0; i--)
	{	
		//break on first slash.
		if(msg->path[i] == '/')
		{
			slashIndex = i;
			break;
		}	
		else if(msg->path[i] == '.')
		{
			dotIndex = i;
		}
	}
	
	/*
	 * if dot does not exist after the last slash, 
	 * assume we have a folder and 
	 * slap index.html onto the end of the name for proxy purposes.
	 */
	if(dotIndex <= slashIndex){
		strcat(retStr, "/");
		strcat(retStr, DEFAULT_FILE_NAME);
	}
	return retStr;
}

void HTTPMsg_prependStringToResource(HTTPMsg *msg, char *strToPrepend)
{
	if(!msg || !strToPrepend) return;
	fprintf(stderr, "prepending path\n");
	if(msg->host)
	{
		char *newPath = calloc(strlen(msg->host) + strlen(strToPrepend) + 2, sizeof(char));
		strcpy(newPath, strToPrepend);
		strcpy(newPath, "/");
		strcpy(newPath, msg->host);
		free(msg->host);
		msg->host = newPath;
	}
	if(msg->path && !msg->host)
	{
		char *newPath = calloc(strlen(msg->path) + strlen(strToPrepend) + 2, sizeof(char));
		strcpy(newPath, strToPrepend);
		strcpy(newPath, "/");
		strcpy(newPath, msg->path);
		free(msg->path);
		msg->path = newPath;
	}
		fprintf(stderr, "prepending resource\n");
	if(msg->resource)
	{
		char *newPath = calloc(strlen(msg->resource) + strlen(strToPrepend) + 2, sizeof(char));
		strcpy(newPath, strToPrepend);
		strcpy(newPath, "/");
		strcpy(newPath, msg->resource);
		free(msg->resource);
		msg->resource = newPath;
	}
}

/**
 * Convenience function for obtaining the resource represented by the message. 
 * @param msg the HTTPMsg
 * @return a new char* containing the resource. 
 */
char* HTTPMsg_getResource(HTTPMsg *msg)
{
	if(!msg) return NULL;
 
	//first, try to use path & host.
	//if(msg->path){
	//	return strdup(msg->path);
	//}
	/*else*/ if(msg->resource){
		return strdup(msg->resource);
	}
	else 
	{
	  return NULL;
	}
}

/**
 * Sends an HTTPMsg.
 * @param msg
 * 		The message to send out. Must be preset with the content-length,
 * 		file* (with the data; if applicable),
 * 		and whether or not it is a request or a response.
 * 		All fields applicable to the type of message must also be set.
 * 		The headers must also be present
 * 		(except content-length which can be END automatically).
 * 		May attempt to send transfer-encoding if not present.
 *
 * @param sock_out_filep the FILE* for writing to the socket.
 * @return 0 on success, or a negative error if FILE* is closed prematurely.
 */
int HTTPMsg_sendMsg(HTTPMsg *msg, FILE* sock_out_filep) {
	if(!msg || !sock_out_filep) return -2;

	//use this line after implementing getReasonPhraseForCode
	char *reasonPhrase = NULL;

	//convert version to string
	char numbuf[NUMBER_BUFFER_SIZE + 1];
	char numbuf2[NUMBER_BUFFER_SIZE + 1];

	char *resource = HTTPMsg_getResource(msg);
	assert(!msg->isRequest || resource);
	snprintf(numbuf, NUMBER_BUFFER_SIZE, "HTTP/%.1f", msg->version);

	//strings for spaces and newlines
	char sp[] = " ";
	char newline[] = "\r\n";
	char colon_space[] = ": ";
	char content_length[] = "Content-Length";
	char xfer_encoding[] = "Transfer-Encoding";
	char chunked[] = "Chunked";

	//print out first line
	if (msg->isRequest) {
		//request format: method SP resource SP HTTP/version
		fwrite(msg->method, sizeof(char), strlen(msg->method), sock_out_filep);
		fwrite(sp, sizeof(char), strlen(sp), sock_out_filep);
		fwrite(resource, sizeof(char), strlen(resource),
				sock_out_filep);
		fwrite(sp, sizeof(char), strlen(sp), sock_out_filep);
		fwrite(numbuf, sizeof(char), strlen(numbuf), sock_out_filep);
		fwrite(newline, sizeof(char), strlen(newline), sock_out_filep);
		fflush(sock_out_filep);
	}

	//WE ARE DOING RESPONSE HERE
	else {
		reasonPhrase = getReasonPhraseForCode(msg->statusCode);

		//get status code
		snprintf(numbuf2, NUMBER_BUFFER_SIZE, "%d", msg->statusCode);

		//output format: HTTP/version SP status-code SP reason-phrase
		fwrite(numbuf, sizeof(char), strlen(numbuf), sock_out_filep);
		fwrite(sp, strlen(sp), 1, sock_out_filep);
		fwrite(numbuf2, sizeof(char), strlen(numbuf2), sock_out_filep);
		fwrite(sp, sizeof(char), strlen(sp), sock_out_filep);
		fwrite(reasonPhrase, sizeof(char), strlen(reasonPhrase),
				sock_out_filep);
		fwrite(newline, sizeof(char), strlen(newline), sock_out_filep);
		fflush(sock_out_filep);
		//DO NOT FREE reason phrase.
	}

	//print out headers
	HeaderListNode *iter = NULL;
	if (msg->headerList) {
		for (iter = msg->headerList->head; iter != NULL; iter = iter->next) {
			//format: Name: Value
			fwrite(iter->name, sizeof(char), strlen(iter->name),
					sock_out_filep);
			fwrite(colon_space, sizeof(char), strlen(colon_space),
					sock_out_filep);
			fwrite(iter->value, sizeof(char), strlen(iter->value),
					sock_out_filep);
			fwrite(newline, sizeof(char), strlen(newline), sock_out_filep);
			fflush(sock_out_filep);
		}
	}

	//if we have a content-length print it out
	if (msg->contentLength > 0) {
		fwrite(content_length, sizeof(char), strlen(content_length),
				sock_out_filep);
		fwrite(colon_space, sizeof(char), strlen(colon_space), sock_out_filep);

		//use the buffer.
		snprintf(numbuf, NUMBER_BUFFER_SIZE - 1, "%d", msg->contentLength);

		//write to file
		fwrite(numbuf, sizeof(char), strlen(numbuf), sock_out_filep);
		fwrite(newline, sizeof(char), strlen(newline), sock_out_filep);
		fflush(sock_out_filep);
	}
	//write transfer encoding header unless already there
	else if (msg->contentLength == CHUNKED_LENGTH) {
		if (HeaderList_doesHeaderExist(msg->headerList, xfer_encoding)) {
			fwrite(xfer_encoding, sizeof(char), strlen(xfer_encoding),
					sock_out_filep);
			fwrite(colon_space, sizeof(char), strlen(colon_space),
					sock_out_filep);
			fwrite(chunked, sizeof(char), strlen(chunked), sock_out_filep);
			fwrite(newline, sizeof(char), strlen(newline), sock_out_filep);
			fflush(sock_out_filep);
		}
	}

	//print out blank line
	fwrite(newline, strlen(newline), 1, sock_out_filep);
	fflush(sock_out_filep);

	//print out body (should be the FILE* in the message)
	char buffer[CHUNK_SIZE + 1];
	int numBytes = 0;
	if (msg->fileptr) {
		rewind(msg->fileptr);
		if (msg->contentLength == CHUNKED_LENGTH) {
			//go through the file, sending CHUNK_SIZE bytes each time.
			while (!feof(msg->fileptr)) {
				numBytes = fread(buffer, sizeof(char), CHUNK_SIZE,
						msg->fileptr);
				if (numBytes <= 0)
					break;

				//chunk len
				snprintf(numbuf, NUMBER_BUFFER_SIZE, "%d\r\n", numBytes);
				fwrite(numbuf, sizeof(char), strlen(numbuf), sock_out_filep);

				//chunk data
				fwrite(buffer, sizeof(char), numBytes, sock_out_filep);

				//newline after chunk
				fwrite(newline, sizeof(char), strlen(newline), sock_out_filep);
				fflush(sock_out_filep);
			}

			//now for the terminating 0 chunk
			//chunk len
			snprintf(numbuf, NUMBER_BUFFER_SIZE, "0\r\n");
			fwrite(numbuf, sizeof(char), strlen(numbuf), sock_out_filep);
		}

		//we are not using chunks
		else {
			//we are not using chunks; just send the entire file.
			while (!feof(msg->fileptr)) {
				numBytes = fread(buffer, sizeof(char), CHUNK_SIZE,
						msg->fileptr);
				if (numBytes <= 0)
					break;
				fwrite(buffer, sizeof(char), numBytes, sock_out_filep);
				fflush(sock_out_filep);
			}
		}
	}
	
	free(resource);
	fwrite(newline, sizeof(char), strlen(newline), sock_out_filep);
	fflush(sock_out_filep);
	return 0;
}

/**
 * Parses the data from sock_fp_in into msg
 * @param msg The HTTPMsg. Must have isRequest set accordingly, and all other variables cleared.
 * @param sock_fp_in the file descriptor for the socket.
 * @return 0 on success, -1 on error, -2 on the FILE*'s EOF.
 */
int HTTPMsg_doParsing(HTTPMsg *msg, FILE* sock_fp_in, bool useFirstFolderAsLoc) {
	int numObtained = 0;
	int numParsed = 0;
	int numRead = 0;
	int chunkLen = 0;
	int bufferLen = BUFFER_SIZE;
	int int_fd = 0;
	char dummyBuffer[BUFFER_SIZE + 1];
	char buffer[BUFFER_SIZE + 1];
	char buffer2[BUFFER_SIZE + 1];
	char buffer3[BUFFER_SIZE + 1];
	bool willNeedConcat = false;
	msg->contentLength = CONTENT_LENGTH_NOT_SET;
	msg->state = FIRSTLINE;
	
	while (1) {
		//now let's get stuff from the socket!
		//first, reset the buffer. 
		memset(buffer, 0, sizeof(buffer));

		//not in body; use fgets 
		if(msg->state != BODY){
			if(fgets(buffer, BUFFER_SIZE, sock_fp_in) == NULL)
			{
				break;
			}
			numObtained = strlen(buffer);

			//remove trailing \r\n
			if (buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
			if (buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
		}

		//in body but +ve content length: use fread
		else if(msg->state == BODY && msg->contentLength > 0){
			numObtained = fread(buffer, sizeof(char), bufferLen, sock_fp_in);
			if(numObtained <= 0){
				break;
			}
		}

		//in body; content length is chunked and chunkLen not set. Use fgets. 
		else if(msg->state == BODY && msg->contentLength == CHUNKED_LENGTH && chunkLen == 0)
		{
			if(fgets(buffer, BUFFER_SIZE, sock_fp_in) == NULL)
			{
				break;
			}
			numObtained = strlen(buffer);

			//remove trailing \r\n
			if (buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
			if (buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
		}

		//in body with chunked content-length and chunkLen > 0. Use fread to get the exact amt of data
		//then use fgets to get rid of the newlines. 
		else if(msg->state == BODY && msg->contentLength == CHUNKED_LENGTH && chunkLen > 0)
		{
			numObtained = fread(buffer, sizeof(char), bufferLen, sock_fp_in);
			if(numObtained <= 0){
				break;
			}
		}

		//we do not have content length set
		else if(msg->state == BODY && msg->contentLength == CONTENT_LENGTH_NOT_SET)
		{
			numObtained = fread(buffer, sizeof(char), BUFFER_SIZE, sock_fp_in);
			if(numObtained <= 0){
				break;
			}
		}

		//well, this should not happen.
		else{
			assert(false);
		}

		//now let's get to the parsing!
		if (msg->state == FIRSTLINE) {
			//response
			if (!msg->isRequest) {
				numParsed = sscanf(buffer, "HTTP/%f %d %s", &(msg->version),
						&(msg->statusCode), buffer2);
				if (numParsed < 3) {
					return -1;
				}

				//strdup to struct
				msg->reasonPhrase = strdup(buffer2);
				fprintf(stderr, "DoParsing: FIRSTLINE complete for RESPONSE. Version %.1f, Code %d, Reason %s\n", 
					msg->version, msg->statusCode, msg->reasonPhrase);
			}

			//request
			else {
				numParsed = sscanf(buffer, "%s %s HTTP/%f", buffer2, buffer3,
						&(msg->version));
				if (numParsed < 3) {
					return -1;
				}
				//strdup to struct
				msg->method = strdup(buffer2);
				msg->resource = strdup(buffer3);
				fprintf(stderr, "DoParsing: FIRSTLINE complete for REQUEST. Version %.1f, Resource %s, Method %s\n", 
					msg->version, msg->resource, msg->method);
			}
			msg->state = HEADERS;
		}

		//head
		else if (msg->state == HEADERS) {
			//blank line: go to body
			if (numObtained <= 0) {
				msg->state = BODY;
				if (msg->isRequest
						&& msg->contentLength == CONTENT_LENGTH_NOT_SET) {
					msg->state = END;
					break;
				} else if (!msg->isRequest && !isBodyOk(msg->statusCode)) {
					msg->state = END;
					break;
				}

				//non-chunked: decrement buffer size as needed. 
				if(msg->contentLength > 0)
				{
					bufferLen = MIN(BUFFER_SIZE, msg->contentLength); 
				}

				fprintf(stderr, "doParsing: HEADERS state complete.\n");
			}

			//space; add to prev
			else if (isspace(buffer[0])) {
				char *iter = buffer;

				//go to first nonwhitespace char?
				while (isspace(*iter))
					iter++;

				//add to previous header
				HeaderList_addToLastHeader(msg->headerList, iter);
			}
			//we do not have space
			else {
				memset(buffer2, 0, sizeof(buffer2));
				memset(buffer3, 0, sizeof(buffer3));
				sscanf(buffer, "%[^ :] %[:] %[^\r\n]", buffer2, dummyBuffer,
						buffer3);

				HeaderList_addHeader(msg->headerList, buffer2, buffer3);
				//check for content length
				if (strcmp(buffer2, "Content-Length") == 0) {
					sscanf(buffer3, "%d", &(msg->contentLength));
				}

				if (strcmp(buffer2, "Transfer-Encoding") == 0) {
					//this means "chunk" unless "identity"
					if (strstr(buffer3, "identity") == NULL) {
						msg->contentLength = CHUNKED_LENGTH;
						printf("We are chunking!\n");
					}
				}
			}
			fprintf(stderr, "doParsing: current tail header: \"%s\", value \"%s\"\n", 
				msg->headerList->tail->name, msg->headerList->tail->value);
		} else if (msg->state == BODY) {
			//we have a body; and a content length too!
			if (msg->contentLength > 0) {
				//add to msg data
				if (msg->fileptr == NULL) {
					msg->fileptr = tmpfile();
				}
				fwrite(buffer, sizeof(char), numObtained, msg->fileptr);
				fflush(msg->fileptr);

				//NOTE: Caller is responsible for closing msg->fileptr.
				numRead += numObtained;
				bufferLen = MIN(BUFFER_SIZE, msg->contentLength - numRead);				
				if (numRead >= msg->contentLength) {
					msg->state = END;
					break;
				}
			}
			//chunky: go until 0 len chunk
			else if (msg->contentLength == CHUNKED_LENGTH) {
				if (chunkLen == 0) {
					printf("about to set chunklen! buffer \"%s\"\n", buffer);
					sscanf(buffer, "%x", &chunkLen);
					printf("We have chunk of %d bytes\n", chunkLen);
					if (chunkLen <= 0) {
						msg->state = END;
					}
					bufferLen = MIN(BUFFER_SIZE, chunkLen);
				} else {
					//add to msg data
					if (msg->fileptr == NULL) {
						msg->fileptr = tmpfile();
					}

					int numWrote = fwrite(buffer, sizeof(char), numObtained, msg->fileptr);
					fflush(msg->fileptr);
					
					//subtract numObtained from chunklen. 		
					chunkLen -= numObtained;
					printf("Read %d bytes. Wrote %d bytes. Rest of chunk is %d bytes\n", numObtained, numWrote, chunkLen);
					
					//if chunklen is zero, do a fgets into dummy buffer to flush out \r\n					
					if(chunkLen <= 0) 
					{
						chunkLen == 0;
						fgets(dummyBuffer, BUFFER_SIZE, sock_fp_in);
					}
					else{
						bufferLen = MIN(BUFFER_SIZE, chunkLen);
					}
				}
			}

			//no idea of length; keep going til eof
			else {
				//add to msg data
				if (msg->fileptr == NULL) {
					msg->fileptr = tmpfile();
				}
				fwrite(buffer, sizeof(char), numObtained, msg->fileptr);
				fflush(msg->fileptr);
			}
		}

		//additional states here
		else {
			break;
		}
	}

	fprintf(stderr, "doParsing: about to load host, path, & port\n");
	HTTPMsg_loadHostPathAndPort(msg, useFirstFolderAsLoc);
	return 0;
}
/******************************************************************************/
/*                           END HTTP STUFF                                   */
/******************************************************************************/

